import Psychro from '..'
import BaseStrategy from './abstract'
import BisectionalMonotonic from '../funcSolve/bisectionalMonotonic'
import type { WetAirState, WetAirStateParameter } from '../types'

/**
 * ### 干球温度和露点温度计算策略
 */
export class DewDryTempture extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    this.validateParams(params, ['dryTemperature', 'dewTemperature'])
    const { dryTemperature, dewTemperature } = params

    this.validateTemperatureOrder(
      dewTemperature,
      dryTemperature,
      'Dew temperature',
      'Dry temperature'
    )

    let wetTemperature: number, relativeHumidity: number
    const pvs = Psychro.calcPvs(dewTemperature)
    const humidity = Psychro.calcHumidity(pvs, pressure)
    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)
    if (dryTemperature === dewTemperature) {
      wetTemperature = dryTemperature
      relativeHumidity = 100
    } else {
      wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
        enthalpy,
        pressure
      )
      relativeHumidity = Psychro.calcRelativeHumidity(
        dryTemperature,
        wetTemperature,
        pressure
      )
    }

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 湿球温度和焓值计算策略
 */
export class DewEnthalpy extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    // 类型守卫：检查参数是否包含所需的属性

    this.validateParams(params, ['dewTemperature', 'enthalpy'])
    const { dewTemperature, enthalpy } = params

    const wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
      enthalpy,
      pressure
    )
    const pv = Psychro.calcPvs(dewTemperature)
    const humidity = Psychro.calcHumidity(pv, pressure)

    const bisc = new BisectionalMonotonic(
      Psychro.calcHumidityFromDryWetTemperature
    )
    const dryTemperature = bisc.ternarySolve(
      humidity,
      [wetTemperature, pressure],
      [wetTemperature, 100]
    )
    const relativeHumidity = Psychro.calcRelativeHumidity(
      dryTemperature,
      wetTemperature,
      pressure
    )

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 湿球温度和相对湿度计算策略
 */
export class DewRelativeHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    // 类型守卫：检查参数是否包含所需的属性

    this.validateParams(params, ['dewTemperature', 'relativeHumidity'])
    const { dewTemperature, relativeHumidity } = params

    const bisc = new BisectionalMonotonic(Psychro.calcDewTemperature)
    const pv = bisc.unarySolve(dewTemperature, [0.01, pressure])
    const humidity = Psychro.calcHumidity(pv, pressure)

    let dryTemperature: number, pvs: number
    if (relativeHumidity === 100) {
      dryTemperature = dewTemperature
      pvs = pv
    } else {
      pvs = (pv * 100) / relativeHumidity
      const bisc2 = new BisectionalMonotonic(Psychro.calcPvs)
      dryTemperature = bisc2.unarySolve(pvs, [dewTemperature, 100])
    }

    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)
    const wetTemperature =
      relativeHumidity === 100
        ? dewTemperature
        : Psychro.resolveWetTemperatureForEnthalpy(enthalpy, pressure)

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 湿球温度和露点温度计算策略
 */
export class DewWetTempture extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    // 类型守卫：检查参数是否包含所需的属性
    this.validateParams(params, ['wetTemperature', 'dewTemperature'])
    const { wetTemperature, dewTemperature } = params
    this.validateTemperatureOrder(
      dewTemperature,
      wetTemperature,
      'Dew temperature',
      'Wet temperature'
    )

    const pvs = Psychro.calcPvs(dewTemperature)
    const humidity = Psychro.calcHumidity(pvs, pressure)
    let dryTemperature: number, relativeHumidity: number
    if (wetTemperature === dewTemperature) {
      dryTemperature = wetTemperature
      relativeHumidity = 100
    } else {
      const bisc = new BisectionalMonotonic(
        Psychro.calcHumidityFromDryWetTemperature
      )
      dryTemperature = bisc.ternarySolve(
        humidity,
        [wetTemperature, pressure],
        [wetTemperature, 100]
      )
      relativeHumidity = Psychro.calcRelativeHumidity(
        dryTemperature,
        wetTemperature,
        pressure
      )
    }
    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 干球温度和焓值计算策略
 */
export class DryEnthalpy extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    this.validateParams(params, ['dryTemperature', 'enthalpy'])
    const { dryTemperature, enthalpy } = params

    const humidity = Psychro.deriveHumidity(enthalpy, dryTemperature)

    const wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
      enthalpy,
      pressure
    )
    const pv = Psychro.derivePv(humidity, pressure)

    const relativeHumidity = Psychro.calcRelativeHumidity(
      dryTemperature,
      wetTemperature,
      pressure
    )
    const dewTemperature = Psychro.calcDewTemperature(pv)

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 干球温度和相对湿度计算策略
 */
export class DryHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    this.validateParams(params, ['dryTemperature', 'humidity'])
    const { dryTemperature } = params
    const humidity = this.normalizeHumidity(params.humidity)

    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)
    const wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
      enthalpy,
      pressure
    )
    const pv = Psychro.derivePv(humidity, pressure)
    const relativeHumidity = Psychro.calcRelativeHumidity(
      dryTemperature,
      wetTemperature,
      pressure
    )
    const dewTemperature = Psychro.calcDewTemperature(pv)

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 干球温度和相对湿度计算策略
 */
export class DryRelativeHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    this.validateParams(params, ['dryTemperature', 'relativeHumidity'])
    const { dryTemperature, relativeHumidity } = params
    const pvs = Psychro.calcPvs(dryTemperature)
    const pv = (pvs * relativeHumidity) / 100
    const humidity = Psychro.calcHumidity(pv, pressure)
    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)

    let wetTemperature: number, dewTemperature: number
    if (relativeHumidity === 100) {
      wetTemperature = dryTemperature
      dewTemperature = dryTemperature
    } else {
      wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
        enthalpy,
        pressure
      )
      dewTemperature = Psychro.calcDewTemperature(pv)
    }

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 干球温度和湿球温度计算策略
 */
export class DryWetTempture extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    this.validateParams(params, ['dryTemperature', 'wetTemperature'])
    const { dryTemperature, wetTemperature } = params
    this.validateTemperatureOrder(
      wetTemperature,
      dryTemperature,
      'Wet temperature',
      'Dry temperature'
    )

    const pv = Psychro.calcPv(dryTemperature, wetTemperature, pressure)
    const humidity = Psychro.calcHumidity(pv, pressure)
    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)
    let dewTemperature: number, relativeHumidity: number
    if (dryTemperature === wetTemperature) {
      dewTemperature = dryTemperature
      relativeHumidity = 100
    } else {
      relativeHumidity = Psychro.calcRelativeHumidity(
        dryTemperature,
        wetTemperature,
        pressure
      )
      dewTemperature = Psychro.calcDewTemperature(pv)
    }

    return {
      dryTemperature,
      wetTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity
    }
  }
}

/**
 * ### 湿度和焓值计算策略
 */
export class EnthalpyHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    // 类型守卫：检查参数是否包含所需的属性
    this.validateParams(params, ['humidity', 'enthalpy'])
    const { enthalpy } = params
    const humidity = this.normalizeHumidity(params.humidity)
    const wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
      enthalpy,
      pressure
    )
    // const d = humidity / 1000
    const pv = Psychro.derivePv(humidity, pressure)
    const dewTemperature = Psychro.calcDewTemperature(pv)

    const dryTemperature = Psychro.deriveDryTemp(enthalpy, humidity)

    const relativeHumidity = Psychro.calcRelativeHumidity(
      dryTemperature,
      wetTemperature,
      pressure
    )
    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 相对湿度和焓值计算策略
 */
export class EnthalpyRelativeHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    // 类型守卫：检查参数是否包含所需的属性
    this.validateParams(params, ['relativeHumidity', 'enthalpy'])
    const { relativeHumidity, enthalpy } = params

    const wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
      enthalpy,
      pressure
    )

    let dryTemperature: number
    if (relativeHumidity === 100) {
      dryTemperature = wetTemperature
    } else {
      const bisc = new BisectionalMonotonic(Psychro.calcRelativeHumidity)
      dryTemperature = bisc.ternarySolve(
        relativeHumidity,
        [wetTemperature, pressure],
        [wetTemperature, 100]
      )
    }

    const pv = Psychro.calcPv(dryTemperature, wetTemperature, pressure)
    const dewTemperature =
      relativeHumidity === 100 ? wetTemperature : Psychro.calcDewTemperature(pv)
    const humidity = Psychro.calcHumidity(pv, pressure)

    return {
      dryTemperature,
      dewTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 相对湿度和湿度比计算策略
 */
export class HumidityRelativeHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    this.validateParams(params, ['humidity', 'relativeHumidity'])
    const { relativeHumidity } = params
    const humidity = this.normalizeHumidity(params.humidity)
    const pv = Psychro.derivePv(humidity, pressure)
    const dewTemperature = Psychro.calcDewTemperature(pv)
    const pvs = (pv * 100) / relativeHumidity

    let wetTemperature: number, dryTemperature: number, enthalpy: number
    if (relativeHumidity === 100) {
      dryTemperature = dewTemperature
      wetTemperature = dewTemperature
      enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)
    } else {
      const bisc = new BisectionalMonotonic(Psychro.calcPvs)
      dryTemperature = bisc.unarySolve(pvs, [0, 100])
      enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)
      wetTemperature = Psychro.resolveWetTemperatureForEnthalpy(
        enthalpy,
        pressure
      )
    }

    return {
      dewTemperature,
      dryTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 湿球温度和湿度计算策略
 */
export class WetHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    // 类型守卫：检查参数是否包含所需的属性
    this.validateParams(params, ['wetTemperature', 'humidity'])
    const { wetTemperature } = params
    const humidity = this.normalizeHumidity(params.humidity)

    const pv = Psychro.derivePv(humidity, pressure)
    let dewTemperature = Psychro.calcDewTemperature(pv)
    if (dewTemperature > wetTemperature) {
      throw new Error(
        ':sad::sad::sad:含湿量对应的露点温度高于湿球温度，传入的参数有误！！！'
      )
    }
    let dryTemperature: number, relativeHumidity: number
    if (wetTemperature - dewTemperature <= Psychro.PRECISION) {
      dewTemperature = wetTemperature
      dryTemperature = wetTemperature
      relativeHumidity = 100
    } else {
      dryTemperature = Psychro.deriveDryTemp(
        Psychro.calcEnthalpy(wetTemperature, humidity),
        humidity
      )
      relativeHumidity = Psychro.calcRelativeHumidity(
        dryTemperature,
        wetTemperature,
        pressure
      )
    }
    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)
    return {
      dewTemperature,
      dryTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}

/**
 * ### 湿球温度和相对湿度计算策略
 */
export class WetRelativeHumidity extends BaseStrategy {
  calculate(params: WetAirStateParameter, pressure: number): WetAirState {
    // 类型守卫：检查参数是否包含所需的属性
    this.validateParams(params, ['wetTemperature', 'relativeHumidity'])
    const { wetTemperature, relativeHumidity } = params
    let dryTemperature: number, dewTemperature: number, pv: number
    if (relativeHumidity === 100) {
      dryTemperature = wetTemperature
      dewTemperature = wetTemperature
      pv = Psychro.calcPv(dryTemperature, wetTemperature, pressure)
    } else {
      const bisc = new BisectionalMonotonic(Psychro.calcRelativeHumidity)
      dryTemperature = bisc.ternarySolve(
        relativeHumidity,
        [wetTemperature, pressure],
        [wetTemperature, 100]
      )
      pv = Psychro.calcPv(dryTemperature, wetTemperature, pressure)
      dewTemperature = Psychro.calcDewTemperature(pv)
    }

    const humidity = Psychro.calcHumidity(pv, pressure)
    const enthalpy = Psychro.calcEnthalpy(dryTemperature, humidity)

    return {
      dewTemperature,
      dryTemperature,
      enthalpy,
      humidity,
      relativeHumidity,
      wetTemperature
    }
  }
}
